package tcp

import (
	"errors"
	"fmt"
	"net"
	"os"
	"time"

	"github.com/kataras/golog"
)

// Server 结构体
type Server struct {
	Name              string
	Address           string
	Listener          *net.TCPListener
	conns             map[string]*net.TCPConn
	logger            *golog.Logger
	shuttingDown      chan bool
	readRetryTimes    int
	readRetryDuration int
	conFn             func(*net.TCPConn, *Server, *golog.Logger)
	disConFn          func(string, bool, *Server, *golog.Logger)
	pkgFn             func(*Package, *net.TCPConn, *Server, *golog.Logger)
}

// NewServer 创建一个 TCPServer 实例
func NewServer(name string, addr string) *Server {
	svr := &Server{
		Name:    name,
		Address: addr,
		conns:   make(map[string]*net.TCPConn),
	}

	return svr
}

// SetLogger 设置日志
func (ts *Server) SetLogger(logger *golog.Logger) {
	ts.logger = logger
}

// Logger 返回日志记录器
func (ts *Server) Logger() *golog.Logger {
	if ts.logger == nil {
		ts.logger = golog.New()
		ts.logger.SetOutput(os.Stdout)
	}

	return ts.logger
}

// Conns 返回已建立连接的链接Map
func (ts *Server) Conns() map[string]*net.TCPConn {
	return ts.conns
}

// Conn 获取指定 key 的connection
func (ts *Server) Conn(key string) *net.TCPConn {
	con, ok := ts.conns[key]
	if !ok {
		return nil
	}
	return con
}

// DeleteConn 删除指定的链接
func (ts *Server) DeleteConn(key string) {
	delete(ts.conns, key)
}

// SetReadRetryParams 设置连接读取的重试参数
func (ts *Server) SetReadRetryParams(times, duration int) {
	ts.readRetryTimes = times
	if ts.readRetryTimes < 0 {
		ts.readRetryTimes = 10
	}
	ts.readRetryDuration = duration
	if ts.readRetryDuration < 0 {
		ts.readRetryDuration = 10
	}
}

// SetOnConnectedFn 设置当有新连接接入时的自定义处理
func (ts *Server) SetOnConnectedFn(fn func(*net.TCPConn, *Server, *golog.Logger)) {
	ts.conFn = fn
}

// SetOnDisconnectFn 设置当有连接中断时的处理，第二个参数为 true 时，为服务端主动关闭连接
func (ts *Server) SetOnDisconnectFn(fn func(string, bool, *Server, *golog.Logger)) {
	ts.disConFn = fn
}

// SetOnPkgFn 设置当解析到数据包进行处理的自定义处理
func (ts *Server) SetOnPkgFn(fn func(*Package, *net.TCPConn, *Server, *golog.Logger)) {
	ts.pkgFn = fn
}

// Start 启动 TCPServer
func (ts *Server) Start() error {
	// 自定义关闭命令
	go OnManualShutdown(ts.shuttingDown, ts.Shutdown)

	var addr *net.TCPAddr
	var err error
	var errMessage string

	// 解析地址
	addr, err = net.ResolveTCPAddr("tcp4", ts.Address)
	if err != nil {
		errMessage = fmt.Sprintf("can resolve tcp address %s", ts.Address)
		ts.Logger().Error(errMessage)
		return errors.New(errMessage)
	}

	// 创建监听器
	ts.Listener, err = net.ListenTCP("tcp", addr)
	if err != nil {
		errMessage = fmt.Sprintf("can listern to tcp %s: %s", ts.Address, err)
		ts.Logger().Error(errMessage)
		return errors.New(errMessage)
	}

	ts.Logger().Infof("%s tcp server's now listerning on %s, wait for connections", ts.Name, ts.Address)

	for len(ts.shuttingDown) < 1 {
		var conn *net.TCPConn
		ts.Listener.SetDeadline(time.Now().Add(60 * time.Second))
		conn, err = ts.Listener.AcceptTCP()
		if err != nil {
			ts.Logger().Infof("accept connection timed out %s, loop", err)
			continue
		}
		// 添加到map
		ts.Logger().Infof("new connection has been accepted %s", conn.RemoteAddr().String())
		ts.conns[conn.RemoteAddr().String()] = conn
		if ts.conFn != nil {
			ts.conFn(conn, ts, ts.Logger())
		} else {
			ts.Logger().Warn("connection on connected handler is not specified")
		}
		go HandleConnWithSig(
			conn,
			ts,
			ts.pkgFn,
			ts.disConFn,
			ts.Logger(),
			ts.readRetryTimes,
			ts.readRetryDuration,
			PackageFirstSigByte,
			PackageSecondSigByte,
		)
	}

	ts.Logger().Infof("%s tcp server has been shutdown", ts.Name)
	return nil
}

// Shutdown 发送关闭消息
func (ts *Server) Shutdown() {
	if ts.shuttingDown == nil {
		return
	}
	if len(ts.shuttingDown) >= 1 {
		return
	}
	// 关闭 conn
	for name, conn := range ts.conns {
		err := conn.Close()
		if err != nil {
			ts.Logger().Warnf("close connection on %s with errors %s", name, err)
		} else {
			ts.Logger().Infof("close connection on %s", name)
		}
	}
	ts.shuttingDown <- true
}
